package com.wilmer.ai.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.unfbx.chatgpt.entity.chat.BaseChatCompletion;
import com.unfbx.chatgpt.entity.chat.BaseMessage;
import com.unfbx.chatgpt.entity.chat.ChatCompletion;
import com.unfbx.chatgpt.entity.chat.Message;
import com.unfbx.chatgpt.exception.BaseException;
import com.wilmer.TecherLessonPlan.preHandle.LessonPlanInterceptor;
import com.wilmer.ai.client.StreamClient;
import com.wilmer.ai.config.AiConfig;
import com.wilmer.ai.config.LocalCache;
import com.wilmer.ai.dto.userChatDto;
import com.wilmer.ai.listener.SseStreamFastGptListener;
import com.wilmer.ai.listener.userSseStreamFastGptListener;
import com.wilmer.ai.request.ChatRequest;
import com.wilmer.ai.response.ChatResponse;
import com.wilmer.ai.service.SseService;
import com.wilmer.ai.utils.aiUtils;
import com.wilmer.aiMessage.domain.AiMessage;
import com.wilmer.aiMessage.mapper.AiMessageMapper;
import com.wilmer.aiUserMsg.domain.YwUserMsg;
import com.wilmer.aiUserMsg.mapper.YwUserMsgMapper;
import com.wilmer.common.core.domain.model.LoginUser;
import com.wilmer.common.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author hans 24426
 */
@Service
@Slf4j
public class SseServiceImpl implements SseService {

    @Resource
    private StreamClient client;
    @Resource
    private AiConfig aiConfig;

    @Resource
    private AiMessageMapper aiMessageMapper;

    @Resource
    private LessonPlanInterceptor lessonPlanInterceptor;

    @Resource
    private YwUserMsgMapper msgMapper;


    @Override
    public SseEmitter createSse(String uid) {
        //默认30秒超时,设置为0L则永不超时
        SseEmitter sseEmitter = new SseEmitter(0L);
        //完成后回调
        sseEmitter.onCompletion(() -> {
            log.info("[{}]结束连接...................", uid);
            LocalCache.CACHE.remove(uid);
        });
        //超时回调
        sseEmitter.onTimeout(() -> {
            log.info("[{}]连接超时...................", uid);
        });
        //异常回调
        sseEmitter.onError(
                throwable -> {
                    try {
                        log.info("[{}]连接异常,{}", uid, throwable.toString());
                        sseEmitter.send(SseEmitter.event()
                                .id(uid)
                                .name("发生异常！")
                                .data(Message.builder().content("发生异常请重试！").build())
                                .reconnectTime(3000));
                        LocalCache.CACHE.put(uid, sseEmitter);
                    } catch (IOException e) {
                        log.info(String.valueOf(e));
                    }
                }
        );
        LocalCache.CACHE.put(uid, sseEmitter);
        log.info("[{}]创建sse连接成功！", uid);
        return sseEmitter;
    }

    @Override
    public void closeSse(String uid) {
        SseEmitter sse = (SseEmitter) LocalCache.CACHE.get(uid);
        if (sse != null) {
            sse.complete();
            //移除
            LocalCache.CACHE.remove(uid);
        }
    }

    @Override
    public ChatResponse sseChat(String uid, ChatRequest chatRequest, HttpServletRequest request) {
        if (StrUtil.isBlank(chatRequest.getMsg())) {
            log.info("参数异常，msg为null", uid);
            throw new BaseException("参数异常，msg不能为空~");
        }
        //将消息存入数据库-这是用户的提问
        this.insertUserMessage(chatRequest.getMsg(), uid, request);
        String messageContext = (String) LocalCache.CACHE.get("msg" + uid);

        List<Message> messages = new ArrayList<>();
        if (StrUtil.isNotBlank(messageContext)) {
            messages = JSONUtil.toList(messageContext, Message.class);
            if (messages.size() >= aiConfig.getMemory()) {
                messages = messages.subList(1, aiConfig.getMemory());
            }
            Message currentMessage = Message.builder().content(chatRequest.getMsg()).role(Message.Role.USER).build();
            messages.add(currentMessage);
        } else {
            Message currentMessage = Message.builder().content(chatRequest.getMsg()).role(Message.Role.USER).build();
            messages.add(currentMessage);
        }
        SseEmitter sseEmitter = (SseEmitter) LocalCache.CACHE.get(uid);
        if (sseEmitter == null) {
            log.info("聊天消息推送失败uid:[{}],没有创建连接，请重试。", uid);
            throw new BaseException("聊天消息推送失败uid:[{}],没有创建连接，请重试。~");
        }
        userSseStreamFastGptListener openAiEventSourceListener = new userSseStreamFastGptListener();
        openAiEventSourceListener.setSseEmitter(sseEmitter);
        ChatCompletion completion = ChatCompletion
                .builder()
                .messages(messages)
                .model(ChatCompletion.Model.GPT_4_VISION_PREVIEW.getName())
                .build();
        //创建流式客户端
        client.openAiStreamClient().streamChatCompletion(completion, openAiEventSourceListener);
        LocalCache.CACHE.put("msg" + uid, JSONUtil.toJsonStr(messages), LocalCache.TIMEOUT);

        ChatResponse response = new ChatResponse();
        response.setQuestionTokens(completion.tokens());
        return response;
    }


    @Override
    public ChatResponse sseChatWithAi(String uid, ChatRequest chatRequest) {
        if (StrUtil.isBlank(chatRequest.getMsg())) {
            log.info("参数异常，msg为null", uid);
            throw new BaseException("参数异常，msg不能为空~");
        }
        //在数据库中获取用户的历史聊天记录
        YwUserMsg userMsg = new YwUserMsg();
        userMsg.setUserId(SecurityUtils.getUserId());
        userMsg.setChatId(uid);
        List<YwUserMsg> userMsgList = msgMapper.selectUserMsgList(userMsg);
        //集合逆序
        aiUtils.reverseListInPlace(userMsgList);
        //构建聊天上下文
        List<Message> messages = new ArrayList<>();
        if (!userMsgList.isEmpty()) {
            for (YwUserMsg ywUserMsg : userMsgList) {
                String msgContext = ywUserMsg.getMsgContext();
                Message message = JSONUtil.toBean(msgContext, Message.class);
                messages.add(message);
            }
        }
        String messageContext = String.valueOf(JSONUtil.parse(messages));

        if (StrUtil.isNotBlank(messageContext.toString())) {
            messages = JSONUtil.toList(messageContext.toString(), Message.class);
            if (messages.size() >= aiConfig.getMemory()) {
                messages = messages.subList(1, aiConfig.getMemory());
            }
            Message currentMessageSys = Message.builder().content("记住你是wilmer,你是STEM课程专家,你不是AI,你需要用markdown语法回复我").role(Message.Role.SYSTEM).build();
            Message currentMessage = Message.builder().content(chatRequest.getMsg()).role(Message.Role.USER).build();

            messages.add(currentMessageSys);
            messages.add(currentMessage);
        } else {
            Message currentMessage = Message.builder().content(chatRequest.getMsg()).role(Message.Role.USER).build();
            Message currentMessageSys = Message.builder().content("记住你是wilmer,你是STEM课程专家，你不是AI你需要用markdown语法回复我").role(Message.Role.SYSTEM).build();
            messages.add(currentMessageSys);
            messages.add(currentMessage);

        }
        SseEmitter sseEmitter = (SseEmitter) LocalCache.CACHE.get(uid);
        if (sseEmitter == null) {
            log.info("聊天消息推送失败uid:[{}],没有创建连接，请重试。", uid);
            throw new BaseException("聊天消息推送失败uid:[{}],没有创建连接，请重试。~");
        }
        userSseStreamFastGptListener openAiEventSourceListener = new userSseStreamFastGptListener();
        openAiEventSourceListener.setSseEmitter(sseEmitter);
        openAiEventSourceListener.setLoginUser(SecurityUtils.getLoginUser());
        openAiEventSourceListener.setMsgMapper(msgMapper);
        openAiEventSourceListener.setChatId(uid);
        openAiEventSourceListener.setMessages(messages);

        ChatCompletion completion = ChatCompletion
                .builder()
                .messages(messages)
                .model(ChatCompletion.Model.GPT_4_VISION_PREVIEW.getName())
                .build();

        //创建流式客户端
        client.openAiStreamClient().streamChatCompletion(completion, openAiEventSourceListener);

        LocalCache.CACHE.put("msg" + uid, JSONUtil.toJsonStr(messages), LocalCache.TIMEOUT);

        ChatResponse response = new ChatResponse();

        response.setQuestionTokens(completion.tokens());

        return response;
    }

    @Transactional
    @Override
    public SseEmitter chatWithAi(userChatDto chatDto) {

        //开启事务将用户的问题写入数据库
        YwUserMsg userMsg = new YwUserMsg();
        userMsg.setDeptId(SecurityUtils.getDeptId());
        userMsg.setUserId(SecurityUtils.getUserId());
        Message currentMessage = Message.builder().content(chatDto.getMsg()).role(Message.Role.USER).build();
        userMsg.setMsgContext(JSONUtil.toJsonStr(currentMessage));
        userMsg.setChatId(chatDto.getChatId());
        msgMapper.insertYwUserMsg(userMsg);
        //更改对话列表名称
        ChatRequest chatRequest = new ChatRequest();
        chatRequest.setMsg(chatDto.getMsg());
        //创建当前客户端sse连接
        SseEmitter sse = createSse(chatDto.getChatId());
        //进行聊天操作
        sseChatWithAi(chatDto.getChatId(), chatRequest);
        return sse;
    }

    public void insertUserMessage(String msg, String uid, HttpServletRequest request) {

        LoginUser user = this.lessonPlanInterceptor.getLoginUser(request);
        AiMessage aiMessage = new AiMessage();
        aiMessage.setMessage(msg);
        aiMessage.setSender(1);
        aiMessage.setCreateDate(new Date());
        aiMessage.setUID(uid);
        aiMessage.setUserId(user.getUserId());
        aiMessage.setUserName(user.getUsername());
        aiMessage.setCreateBy(user.getUsername());
        this.aiMessageMapper.insertAiMessage(aiMessage);

    }

}
